from time import sleep
from web3 import Web3
from web3.contract import Contract
from json import loads
from traceback import print_exc
from sqlalchemy import Column, BigInteger, String, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from os import environ

FACTORY_ADDRESS = '0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73'
FACTORY_ABI = loads('[{"inputs":[{"internalType":"address","name":"_feeToSetter","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token0","type":"address"},{"indexed":true,"internalType":"address","name":"token1","type":"address"},{"indexed":false,"internalType":"address","name":"pair","type":"address"},{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"PairCreated","type":"event"},{"constant":true,"inputs":[],"name":"INIT_CODE_PAIR_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allPairs","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"allPairsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"}],"name":"createPair","outputs":[{"internalType":"address","name":"pair","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"feeTo","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"feeToSetter","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"getPair","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_feeTo","type":"address"}],"name":"setFeeTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_feeToSetter","type":"address"}],"name":"setFeeToSetter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]')
RPC_URL = 'https://bsc-dataseed1.ninicoin.io'
SYNCED_INDEX_FILE = '.synced_index'



class PancakePair(declarative_base()):
  __tablename__ = 'pancakepair'

  id = Column(BigInteger, primary_key=True, index=True)
  address = Column(String(256))

class SyncedIndex:

  @staticmethod
  def get() -> int:
    try:
      with open(SYNCED_INDEX_FILE) as f:
        res = int(f.read())
      return res
    except Exception as e:
      print(f'error occured while SyncedIndex.get')
      print(e)
      print_exc()
  
  @staticmethod
  def set(i):
    try:
      with open(SYNCED_INDEX_FILE, 'w') as f:
        f.write(str(i))
    except Exception as e:
      print(f'error occured while SyncedIndex.set')
      print(e)
      print_exc()

def sync_index(index, factory_contract: Contract, mysql_sessionmaker: sessionmaker):
  print(f'To sync {index}')
  pair_address = factory_contract.functions.allPairs(index).call()
  
  new_pair = PancakePair(id=index, address=pair_address)

  session = mysql_sessionmaker()
  session.add(new_pair)
  session.commit()
  session.close()
  SyncedIndex.set(index)

if __name__ == '__main__':
  # database
  password = environ.get('password')
  engine = create_engine(f'mysql+pymysql://root:{password}@localhost:3306/bsc_chain')
  mysql_sessionmaker = sessionmaker(bind=engine)

  # web3
  w3 = Web3(Web3.HTTPProvider(RPC_URL))
  factory_contract = w3.eth.contract(FACTORY_ADDRESS, abi=FACTORY_ABI)
  
  while True:
    len_pair = factory_contract.functions.allPairsLength().call()
    index_max = len_pair - 1
    synced_index = SyncedIndex.get()

    if synced_index >= index_max:
      print(f'{len_pair} pairs synced, now waiting...')
      sleep(5)
      continue
    
    for index in range(synced_index + 1, len_pair):
      sync_index(index, factory_contract, mysql_sessionmaker)

    

